<?php
/**
 * +-------------------------------------------------------------
 * |Caption:
 * +-------------------------------------------------------------
 * |Author: Lenny
 * +-------------------------------------------------------------
 * |Email:  1685964109@qq.com
 * +-------------------------------------------------------------
 * |Date:   2024/6/21 上午11:19
 * +-------------------------------------------------------------
 */

namespace app\admin\middleware;

use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use app\admin\service\SystemLogService;
use app\common\traits\JsonTrait;
use Closure;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\DocParser;
use ReflectionClass;

class SystemLog
{
    use JsonTrait;

    /**
     * 敏感信息字段，日志记录时需要加密
     * @var array
     */
    protected array $sensitiveParams = [
            'password',
            'password_again',
            'phone',
            'mobile',
    ];

    public function handle($request, Closure $next)
    {
        // 获取请求参数
        $params = $request->param();
        // 如果请求参数中有's'，则移除它
        if (isset($params['s'])) unset($params['s']);
        // 遍历请求参数，如果参数名在敏感参数列表中，则将其值替换为隐藏字符
        foreach ($params as $key => $val) {
            in_array($key, $this->sensitiveParams) && $params[$key] = "***********";
        }
        // 获取请求的HTTP方法，并转换为小写
        $method = strtolower($request->method());
        // 获取请求的完整URL
        $url    = $request->url();

        // 如果开启了应用的调试模式，则记录请求的详细信息
        if (env('APP_DEBUG')) {
            trace(['url' => $url, 'method' => $method, 'params' => $params,], 'requestDebugInfo');
        }
        // 调用下一个中间件或最终的请求处理器
        $response = $next($request);
        // 如果请求方法为POST、PUT或DELETE，则记录日志
        if (in_array($method, ['post', 'put', 'delete'])) {
            // 初始化日志标题变量
            $title = '';
            try {
                // 获取请求的控制器和方法
                $_controller = $request->controller();
                $_action     = $request->action();
                if ($_controller && $_action) {
                    // 构造完整的控制器类路径
                    $className       = "app\admin\controller\\{$_controller}";
                    // 获取类的反射对象
                    $reflectionClass = new ReflectionClass($className);
                    // 创建文档注解解析器
                    $parser          = new DocParser();
                    // 设置解析器忽略未导入的注解
                    $parser->setIgnoreNotImportedAnnotations(true);
                    // 创建注解阅读器
                    $reader               = new AnnotationReader($parser);
                    // 获取控制器的注解
                    $controllerAnnotation = $reader->getClassAnnotation($reflectionClass, ControllerAnnotation::class);
                    // 获取动作的方法对象
                    $reflectionAction     = $reflectionClass->getMethod($_action);
                    // 获取动作的注解
                    $nodeAnnotation       = $reader->getMethodAnnotation($reflectionAction, NodeAnnotation::class);
                    $title                = $controllerAnnotation->title . ' - ' . $nodeAnnotation->title;
                }
            }catch (\Throwable $e) {
                // 异常捕获，不中断程序执行
                return $this->errorJson('系统错误，请稍后再试'.$e->getMessage().'-位置-'.$e->getLine().'-文件-'.$e->getFile());
            }
            // 获取请求的IP地址
            $ip   = $request->server('HTTP_X_FORWARDED_FOR', $request->ip());
            // 准备日志数据数组
            $data = [
                    'admin_id'    => session('admin.id'),    // 管理员ID
                    'title'       => $title,                       // 日志标题
                    'url'         => $url,                         // 请求URL
                    'method'      => $method,                      // 请求方法
                    'ip'          => $ip,                          // 请求IP
                    'content'     => json_encode($params, JSON_UNESCAPED_UNICODE),              // 请求参数
                    'response'    => json_encode($response->getData(), JSON_UNESCAPED_UNICODE), // 响应数据
                    'useragent'   => $request->server('HTTP_USER_AGENT'),  // 用户代理
                    'create_time' => time(),                  // 创建时间
            ];
            // 保存日志数据
            SystemLogService::instance()->save($data);
        }
        // 返回响应对象，继续执行后续的中间件或返回响应给客户端
        return $response;
    }
}